// Copyright (c) 2025 Shenzhen Kaihong Digital Industry Development Co., Ltd.
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

use std::{fs::Permissions, os::unix::fs::PermissionsExt, path::{Path, PathBuf}};

use anyhow::{Context, Ok};
use nix::mount::MsFlags;

use crate::{build::chroot::{chroot_cmd, chroot_mount_setup, nix_mount, pre_chroot_setup, umount_targets}, database::local::{CACHE_ROOT, PKGS_ROOT}, deploy::{fhs::build_fhs, merge::merge_dir_all, MergeMode}, utils::random_tmp_path};

async fn ferrium_init(root: &PathBuf, src_dir: &Path, targets: &mut Vec<PathBuf>) -> anyhow::Result<()> {
    let mut cmd = chroot_cmd(root.as_path(), None, false, false);
    cmd.arg("LD_LIBRARY_PATH=/usr/lib");
    cmd.arg("ferrium");
    cmd.arg("init");
    cmd.arg( "--config-root");
    cmd.arg(&format!("{}/config", src_dir.display()));
    cmd.arg("--pkgs-root");
    cmd.arg(&format!("{}", PKGS_ROOT.display()));
    cmd.arg("--cache-root");
    cmd.arg(&format!("{}", CACHE_ROOT.display()));
    cmd.arg("--shell-configs");
    cmd.arg("bash");
    cmd.arg("--local-config");
    cmd.arg(&format!("{}/config", src_dir.display()));
    let status = cmd
        .spawn()?
        .wait()
        .await?;
    if !status.success() {
        if !umount_targets(&root, targets).await {
            return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
        }
        return Err(anyhow::anyhow!("init chroot command failed with status {}", status));
    }
    log::info!("ferrium init success.");
    Ok(())
}

async fn ferrium_install(root: &PathBuf, src_dir: &Path, targets: &mut Vec<PathBuf>, extra_args: &Vec<String>) -> anyhow::Result<()> {
    let mut cmd = chroot_cmd(root.as_path(), None, false, false);
    cmd.arg("LD_LIBRARY_PATH=/usr/lib");
    cmd.arg(&format!("CONFIG_ROOT={}/config", src_dir.display()));
    cmd.arg(&format!("PKGS_ROOT={}", PKGS_ROOT.display()));
    cmd.arg(&format!("CACHE_ROOT={}", CACHE_ROOT.display()));
    cmd.arg("FAKE_HOST_ARCH=x86_64");
    cmd.arg("ferrium");
    cmd.arg("install");
    cmd.args(extra_args);
    let status = cmd
        .spawn()?
        .wait()
        .await?;
    if !status.success() {
        if !umount_targets(&root, targets).await {
            return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
        }
        return Err(anyhow::anyhow!("install chroot command failed with status {}", status));
    }
    log::info!("ferrium install success.");
    Ok(())
}

async fn ferrium_deploy(root: &PathBuf, src_dir: &Path, dst_dir: &Path, targets: &mut Vec<PathBuf>, extra_args: &Vec<String>) -> anyhow::Result<()> {
    let mut cmd = chroot_cmd(root.as_path(), None, false, false);
    cmd.arg("LD_LIBRARY_PATH=/usr/lib");
    cmd.arg(&format!("CONFIG_ROOT={}/config", src_dir.display()));
    cmd.arg(&format!("PKGS_ROOT={}", PKGS_ROOT.display()));
    cmd.arg(&format!("CACHE_ROOT={}", CACHE_ROOT.display()));
    cmd.arg("FAKE_HOST_ARCH=x86_64");
    cmd.arg("ferrium");
    cmd.arg("deploy");
    cmd.arg("--root");
    cmd.arg(&format!("{}", dst_dir.display()));
    cmd.arg("--single-arch");
    cmd.arg("--arch");
    cmd.arg("aarch64");
    cmd.args(extra_args);
    let status = cmd
        .spawn()?
        .wait()
        .await?;
    if !status.success() {
        if !umount_targets(&root, targets).await {
            return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
        }
        return Err(anyhow::anyhow!("depoly chroot command failed with status {}", status));
    }
    log::info!("ferrium deploy success.");
    Ok(())
}

pub async fn run_setup(src_dir: &Path, dst_dir: &Path, extra_args: &Vec<String>) -> anyhow::Result<()> {
    let root = random_tmp_path::<3>().context("failed to create temp dir")?;
    std::fs::create_dir(&root)?;
    std::fs::set_permissions(&root, Permissions::from_mode(0o777))?;

    build_fhs(&root).await?;
    pre_chroot_setup(&root, false, None, false, false, false).await
        .context("failed to setup for chroot")?;

    let mut mount_targets = chroot_mount_setup(&root).await
        .map_err(|_| anyhow::anyhow!("failed to setup chroot mount targets"))?;
    nix_mount(&mut mount_targets, &root, "/data", "data", None, MsFlags::MS_BIND)
        .await.map_err(|_| anyhow::anyhow!("failed to setup data mount targets"))?;

    std::fs::create_dir_all(root.join("etc/ssl/certs"))?;
    merge_dir_all(Path::new("/etc/ssl/certs").to_path_buf(), root.join("etc/ssl/certs"), MergeMode::Copy)?;
    merge_dir_all(src_dir.join("bin"), root.join("usr/bin"), MergeMode::Copy)?;
    merge_dir_all(src_dir.join("lib"), root.join("usr/lib"), MergeMode::Copy)?;

    ferrium_init(&root, src_dir, &mut mount_targets).await?;
    ferrium_install(&root, src_dir, &mut mount_targets, extra_args).await?;
    ferrium_deploy(&root, src_dir, dst_dir, &mut mount_targets, extra_args).await?;

    if !umount_targets(&root, &mut mount_targets).await {
        return Err(anyhow::anyhow!("failed to clear chroot mount targets"));
    }

    Ok(())
}
